AddCSLuaFile( "cl_init.lua" )
AddCSLuaFile( "shared.lua" )

include('shared.lua')

function ENT:Initialize()
	self.Entity:SetModel('models/props_junk/harpoon002a.mdl')

	self.Entity:PhysicsInit( SOLID_VPHYSICS )
	self.Entity:SetMoveType( MOVETYPE_VPHYSICS )
	self.Entity:SetSolid( SOLID_VPHYSICS )

	local phys = self.Entity:GetPhysicsObject()
	
	if (phys:IsValid()) then
		phys:Wake()
	end

end

function ENT:GetLength()
	if self.Stuck then
		return self.length
	else
		local ln = self.launcher
		if !ln or !ln:IsValid() then return nil end
		return ((ln:GetPos() - self.Entity:GetPos()):Length())
	end
end

function ENT:Think()
	self.lastThink = self.lastThink or CurTime()
	local dt = CurTime() - self.lastThink
	self.lastThink = CurTime()
	
	if not self.Stuck then
		local phys = self.Entity:GetPhysicsObject()
		local ov = phys:GetVelocity()
		phys:SetAngle(ov:Angle():Forward():Angle())
		phys:SetVelocity(ov)
	end
	
	if self.Stuck and self.constraint and self.dir and self.dir != 0 then
		self.length = self.length + (self.dir * self.reelspeed * dt)
		self.constraint:Fire('SetSpringLength',tostring(self.length),0)
	end
end

function ENT:Constrain()
	local distance = self:GetLength() or 0
	self.length = distance + 15

	local wpos1 = self.Entity:GetPos() + (self.Entity:GetForward() * self.Entity:OBBMins().x)
	local wpos2 = self.launcher:GetPos() + (self.launcher:GetUp() * self.launcher:OBBMaxs().z)
	local constraint = ents.Create("phys_spring")
		constraint:SetPos( wpos1 )
		constraint:SetKeyValue( "springaxis", tostring(wpos2) )
		constraint:SetKeyValue( 'constant', 5000 )
		constraint:SetKeyValue( 'damping', 100 )
		constraint:SetKeyValue( 'rdamping', 500 )
		constraint:SetKeyValue( 'spawnflags', 1)
		constraint:SetPhysConstraintObjects( self.Entity:GetPhysicsObject(), self.launcher:GetPhysicsObject() )
	constraint:Spawn()
	constraint:Activate()
	constraint:Fire("SetSpringLength", distance + 15, 0)


	self.constraint = constraint
end

function ENT:PhysicsUpdate( physobj )
	local distance = self:GetLength() or 0

	if not self.Stuck  then
		distance = distance + 200 -- cut us some slack so it looks cooler as it flies :P
	end

	local rope = self.rope
	if rope and rope:IsValid() then
		rope:Fire('SetLength',tostring(distance),0.0)
	end
	
	if self.StuckData then
		local ent = self.StuckData[1]
		local bone = self.StuckData[2]
		local pos = self.StuckData[3]


		self.StuckData = nil
		
		physobj:SetPos(pos)

		self.weld = constraint.Weld( ent, self.Entity, bone, 0, 0 )
		self.Entity:GetPhysicsObject():EnableCollisions( false ) -- no more sticking stuff.. hopefully stabilizes something :P
		if not util.tobool(self.noconstraint) then
			self:Constrain()
		end
	end
end

function ENT:DuplicateRagdoll(ent)
	if ent:IsPlayer() or ent:IsNPC() then
		local rd = ents.Create('prop_ragdoll')
			rd:SetModel(ent:GetModel())
			rd:SetPos(ent:GetPos())
			rd:SetAngles(ent:GetAngles())
		rd:Spawn()
		rd:Activate()
		
		rd:Fire('Kill','',30.0) -- 30 seconds should be plenty of time.
		
		return rd
	end
	
	return nil
end

function ENT:PhysicsCollide( data, phys )
	local target = data.HitEntity
	local bone = nil
	local pos = data.HitPos
	
	if self.Stuck then return end

	if target:IsWorld() then
		bone = 0
	else
		if !target or !target:IsValid() then 
			print('Invalid Entity')
			return 
		end
		
		local tphys = data.HitObject
		if !tphys or !tphys:IsValid() then 
			print('Invalid Physics')
			return 
		end
		
		bone = 0
	end

	self.length = self:GetLength() -- gotta set this before stucking ourselves :P
	self.Stuck = true
	
	if target:IsPlayer() or target:IsNPC() then
		local newtarget = self:DuplicateRagdoll(target)

		if target:IsNPC() then
			target:Fire('Kill','',0.0)
		end

		target = newtarget

		if target:GetPhysicsObjectCount() == 15 then -- Valid player models (usually. Funny results if it's something weird with 15 bones :)
			local funnybones = { 0, 1, 10 } -- Crotch, Chest, Head
			bone = funnybones[math.random(1,3)]
			pos = target:GetPhysicsObjectNum(bone):GetPos()
		end
	end
	
	self.StuckData = { target, bone, pos }
end

